Overview of tutorial

This tutorial simulates a population effect size of Cohen’s d = 0.5 for different sample sizes, and examines the relationship between Cohen’s d, its 95% Confidence Interval, and the significance of the t-test’s p-value.

By the end of this lesson you should understand that p-values are re-expressions of the same information conveyed by Confidence Intervals, and that statistical power is a re-expression of the width of Confidence Intervals.

Citation & License

Citation:

Ian Hussey (2024) Improving your statistical inferences through simulation studies in R. https://github.com/ianhussey/simulation-course

License:

CC BY 4.0

Dependencies

library(tidyr)
library(dplyr)
library(purrr) 
library(stringr)
library(forcats)
library(ggplot2)
library(scales)
library(patchwork)
library(knitr)
library(kableExtra)
library(janitor)
library(effsize)

Simulation

# functions for simulation
generate_data <- function(n_per_condition,
                          mean_control,
                          mean_intervention,
                          sd) {
  
  data_control <- 
    tibble(condition = "control",
           score = rnorm(n = n_per_condition, mean = mean_control, sd = sd))
  
  data_intervention <- 
    tibble(condition = "intervention",
           score = rnorm(n = n_per_condition, mean = mean_intervention, sd = sd))
  
  data_combined <- bind_rows(data_control,
                             data_intervention) |>
    mutate(condition = fct_relevel(condition, "intervention", "control"))
  
  return(data_combined)
}

analyze <- function(data) {

  res_t_test <- t.test(formula = score ~ condition, 
                       data = data,
                       var.equal = TRUE,
                       alternative = "two.sided")
  
  res_cohens_d <- effsize::cohen.d(formula = score ~ condition,
                                   data = data,
                                   pooled = TRUE)
  
  res <- tibble(p = res_t_test$p.value, 
                cohens_d = res_cohens_d$estimate,
                cohens_d_ci_lower = res_cohens_d$conf.int[1],
                cohens_d_ci_upper = res_cohens_d$conf.int[2])

  return(res)
}


# set seed
set.seed(42)

# simulation parameters
experiment_parameters <- expand_grid(
  n_per_condition = seq(from = 10, to = 90, by = 10),
  mean_control = 0,
  mean_intervention = 0.5,
  sd = 1,
  iteration = 1:1000
) 

# run simulation
simulation <- experiment_parameters |>
  mutate(generated_data = pmap(list(n_per_condition, 
                                    mean_control,
                                    mean_intervention,
                                    sd),
                               generate_data)) |>
  mutate(results = pmap(list(generated_data),
                        analyze))

Cohen’s d by sample size

# wrangle
simulation |>
  unnest(results) |>
  # plot
  ggplot(aes(n_per_condition*2, cohens_d)) +
  geom_jitter(alpha = 0.25) +
  geom_hline(yintercept = 0, linetype = "dotted") +
  scale_x_continuous(breaks = breaks_pretty(n = 10),
                     name = "Total N") +
  scale_y_continuous(breaks = breaks_pretty(n = 10),
                     #limits = c(0,1),
                     name = "Cohen's d") +
  theme_linedraw() +
  scale_color_viridis_d(begin = 0.3, end = 0.7) +
  ggtitle("Population Cohen's d = 0.5")

  • Each point is a Cohen’s d from one dataset (i.e., one iteration).
  • The population Cohen’s d is 0.5. Notice how the distribution of sample Cohen’s d values vary by sample size: Cohen’s d values above 2.0 and below -0.5 are observed when sample size is very small. As sample size gets larger, they stabilize. However, individual datasets still generate Cohen’s d values that diverge substantially from the true value of 0.5.

Ordered by statistical significance

Let’s color the Cohen’s ds by their statistical significance.

# wrangle
simulation |>
  unnest(results) |>
  mutate(significant = p < .05) |>
  # plot
  ggplot(aes(n_per_condition*2, cohens_d, color = significant)) +
  geom_jitter(alpha = 0.25) +
  geom_hline(yintercept = 0, linetype = "dotted") +
  scale_x_continuous(breaks = breaks_pretty(n = 10),
                     name = "Total N") +
  scale_y_continuous(breaks = breaks_pretty(n = 10),
                     #limits = c(0,1),
                     name = "Cohen's d") +
  theme_linedraw() +
  scale_color_viridis_d(begin = 0.3, end = 0.7, direction = -1) +
  ggtitle("Population Cohen's d = 0.5")

  • There is clearly a cut-off value for each sample size that determines when a Cohen’s d value is associated with a significant vs. non-significant t-test p-value. What determines this cut-off? Do you have an intuition for it?

Average effect size for significant effects

Quick aside: Significant results are more likely to be published than non significant ones. If we only look at the significant results, what is the average effect size by N?

Plot only the significant effect sizes, and add their means.

# wrangle
simulation |>
  unnest(results) |>
  mutate(significant = p < .05) |>
  filter(significant) |>
  # plot
  ggplot(aes(n_per_condition*2, cohens_d, color = significant)) +
  geom_jitter(alpha = 0.25) +
  # add mean points 
  stat_summary(fun = "mean",
               geom = "point",
               size = 3,
               color = "#35608DFF") +
  geom_hline(yintercept = 0, linetype = "dotted") +
  scale_x_continuous(breaks = breaks_pretty(n = 10),
                     name = "Total N") +
  scale_y_continuous(breaks = breaks_pretty(n = 10),
                     #limits = c(0,1),
                     name = "Cohen's d") +
  theme_linedraw() +
  scale_color_viridis_d(begin = 0.3, end = 0.7, direction = -1) +
  ggtitle("Population Cohen's d = 0.5")

  • Back to the q: There is clearly a cut-off value for each sample size that determines when a Cohen’s d value is associated with a significant vs. non-significant t-test p-value. What determines this cut-off? Do you have an intuition for it?

Cohen’s d and its 95% CIs

To understand it further, let’s add the 95% Confidence Intervals.

Randomly select one dataset per sample size

To understand the plot, we’ll plot just one data set (iteration) for each sample size.

Re-run the chunk to select new random values. Notice how the magnitude of the Cohen’s d values ‘dance’ around between data sets due to random sampling error, but the width of the Confidence Intervals do not. Confidence width is steady as it is a function of sample size.

# wrangle
simulation |>
  unnest(results) |>
  mutate(significant = p < .05) |>
  # randomly sample 1 effects size per sample size
  group_by(n_per_condition) |>
  slice_sample(n = 1) |>
  ungroup() |>
  # plot
  ggplot(aes(n_per_condition*2, cohens_d, color = significant)) +
  geom_point() +
  # 95% CIs
  geom_linerange(aes(ymin = cohens_d_ci_lower, ymax = cohens_d_ci_upper)) +
  geom_hline(yintercept = 0, linetype = "dotted") +
  scale_x_continuous(breaks = breaks_pretty(n = 10),
                     name = "Total N") +
  scale_y_continuous(breaks = breaks_pretty(n = 10),
                     #limits = c(0,1),
                     name = "Cohen's d") +
  theme_linedraw() +
  scale_color_viridis_d(begin = 0.3, end = 0.7) +
  ggtitle("Population Cohen's d = 0.5\nOne randomly selected dataset per sample size")

  • Significant when 95% CI excludes zero effect size

All datasets

Now let’s plot all the data sets (iterations).

# wrangle
simulation |>
  unnest(results) |>
  mutate(significant = p < .05,
         total_n = paste("N =", n_per_condition*2),
         total_n = fct_reorder(total_n, as.numeric(str_extract(total_n, "\\d+")))) |>
  # plot
  ggplot(aes(iteration, cohens_d, color = significant)) +
  geom_point(alpha = 0.5) +
  geom_linerange(aes(ymin = cohens_d_ci_lower, ymax = cohens_d_ci_upper), alpha = 0.2) +
  geom_hline(yintercept = 0, linetype = "dotted") +
  scale_x_continuous(breaks = breaks_pretty(n = 10),
                     name = "Iteration") +
  scale_y_continuous(breaks = breaks_pretty(n = 10),
                     #limits = c(0,1),
                     name = "Cohen's d") +
  theme_linedraw() +
  scale_color_viridis_d(begin = 0.3, end = 0.7) +
  facet_wrap(~ total_n, ncol = 3, scales = "free_y")

Ordered by Cohen’s d

The above plot is hard to understand. Let’s order the Cohen’s ds from smallest to largest, so the small ones are on the left and the large ones are on the right.

# wrangle
simulation |>
  unnest(results) |>
  mutate(significant = p < .05,
         total_n = paste("N =", n_per_condition*2),
         total_n = fct_reorder(total_n, as.numeric(str_extract(total_n, "\\d+")))) |>
  arrange(n_per_condition, cohens_d) |>
  group_by(n_per_condition) |>
  mutate(rank = row_number()) |>
  ungroup() |>
  # plot
  ggplot(aes(rank, cohens_d, color = significant)) +
  geom_point() +
  geom_linerange(aes(ymin = cohens_d_ci_lower, ymax = cohens_d_ci_upper), alpha = 0.2) +
  geom_hline(yintercept = 0, linetype = "dotted") +
  scale_x_continuous(breaks = breaks_pretty(n = 10),
                     name = "Ranked iteration") +
  scale_y_continuous(breaks = breaks_pretty(n = 10),
                     #limits = c(0,1),
                     name = "Cohen's d") +
  theme_linedraw() +
  scale_color_viridis_d(begin = 0.3, end = 0.7) +
  facet_wrap(~ total_n, ncol = 3)

  • Notice the relationship between 95% CI and p value significance.
  • A certain percentage of Cohen’s ds are green vs. blue. What is this percentage also called? What statistical property?

Mean Cohen’s d and mean interval width by sample size

Now let’s average over the Cohen’s ds in each condition to find the mean Cohen’s d and its mean 95% CIs.

# wrangle
simulation_summary <- simulation |>
  # unnest results
  unnest(results) |>
  group_by(n_per_condition) |>
  summarize(proportion_significant = mean(p < .05), 
            mean_cohens_d = mean(cohens_d),
            mean_cohens_d_ci_lower = mean(cohens_d_ci_lower),
            mean_cohens_d_ci_upper = mean(cohens_d_ci_upper)) |>
  mutate(centered_mean_cohens_d_ci_lower = mean_cohens_d_ci_lower - mean_cohens_d,
         centered_mean_cohens_d_ci_upper = mean_cohens_d_ci_upper - mean_cohens_d)

# plot results
p1 <- ggplot(simulation_summary, aes(n_per_condition*2, mean_cohens_d)) +
  geom_point() +
  geom_linerange(aes(ymin = mean_cohens_d_ci_lower, ymax = mean_cohens_d_ci_upper)) +
  geom_hline(yintercept = 0, linetype = "dotted") +
  scale_x_continuous(breaks = breaks_pretty(n = 10),
                     name = "Total sample size") +
  scale_y_continuous(breaks = breaks_pretty(n = 5),
                     #limits = c(0,1),
                     name = "Mean Cohen's d\n(and mean 95% CIs)") +
  theme_linedraw() +
  ggtitle("Population Cohen's d = 0.5")

p1

Mean Cohen’s d and power by sample size

p2 <- ggplot(simulation_summary, aes(n_per_condition*2, proportion_significant)) +
  geom_point() +
  geom_hline(yintercept = 0.05, linetype = "dotted") +
  geom_hline(yintercept = 0.80, linetype = "dotted") +
  scale_x_continuous(breaks = breaks_pretty(n = 10),
                     name = "Total sample size") +
  scale_y_continuous(breaks = breaks_pretty(n = 5),
                     limits = c(0,1),
                     name = "Proportion of significant\np-values") +
  theme_linedraw() 

p1 + p2 + plot_layout(ncol = 1)

Exercise: Power for equivalence test

See Lakens et al., 2018 for a tutorial on the concepts below.

Using a Smallest Effect Size of Interest (SESOI) of Cohen’s d = 0.2, what is the power of a Two One-Sided Equivalence Test (TOST) for different sample sizes? What N is needed for 80% power to detect a true null effect size as equivalent to zero?

Note: because reasons, use the 90% Confidence Interval instead of 95 (see Lakens et al., 2018).

if(file.exists("materials/lakens et al 2018 figure 1.png")){
  knitr::include_graphics("materials/lakens et al 2018 figure 1.png")
}

Session info

sessionInfo()
## R version 4.3.3 (2024-02-29)
## Platform: aarch64-apple-darwin20 (64-bit)
## Running under: macOS 15.4
## 
## Matrix products: default
## BLAS:   /Library/Frameworks/R.framework/Versions/4.3-arm64/Resources/lib/libRblas.0.dylib 
## LAPACK: /Library/Frameworks/R.framework/Versions/4.3-arm64/Resources/lib/libRlapack.dylib;  LAPACK version 3.11.0
## 
## locale:
## [1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
## 
## time zone: Europe/Zurich
## tzcode source: internal
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
##  [1] effsize_0.8.1        janitor_2.2.1        kableExtra_1.4.0    
##  [4] knitr_1.49           patchwork_1.2.0.9000 scales_1.3.0        
##  [7] ggplot2_3.5.1        forcats_1.0.0        stringr_1.5.1       
## [10] purrr_1.0.4          dplyr_1.1.4          tidyr_1.3.1         
## 
## loaded via a namespace (and not attached):
##  [1] gtable_0.3.6      jsonlite_1.8.9    compiler_4.3.3    tidyselect_1.2.1 
##  [5] xml2_1.3.6        snakecase_0.11.1  jquerylib_0.1.4   png_0.1-8        
##  [9] systemfonts_1.0.6 yaml_2.3.10       fastmap_1.2.0     R6_2.6.1         
## [13] generics_0.1.3    tibble_3.2.1      munsell_0.5.1     lubridate_1.9.4  
## [17] svglite_2.1.3     bslib_0.8.0       pillar_1.10.1     rlang_1.1.5      
## [21] cachem_1.1.0      stringi_1.8.4     xfun_0.49         sass_0.4.9       
## [25] timechange_0.3.0  viridisLite_0.4.2 cli_3.6.4         withr_3.0.2      
## [29] magrittr_2.0.3    digest_0.6.37     grid_4.3.3        rstudioapi_0.17.1
## [33] lifecycle_1.0.4   vctrs_0.6.5       evaluate_1.0.1    glue_1.8.0       
## [37] farver_2.1.2      colorspace_2.1-1  rmarkdown_2.29    tools_4.3.3      
## [41] pkgconfig_2.0.3   htmltools_0.5.8.1
LS0tCnRpdGxlOiAiVW5kZXJzdGFuZGluZyB0aGUgbGluayBiZXR3ZWVuIHAtdmFsdWVzLCBDb25maWRlbmNlIEludGVydmFscywgYW5kIHBvd2VyIgphdXRob3I6ICJJYW4gSHVzc2V5IgpkYXRlOiAiYHIgZm9ybWF0KFN5cy50aW1lKCksICclZCAlQiwgJVknKWAiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgY29kZV9mb2xkaW5nOiBzaG93CiAgICBoaWdobGlnaHQ6IGhhZGRvY2sKICAgIHRoZW1lOiBmbGF0bHkKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwotLS0KCiMgT3ZlcnZpZXcgb2YgdHV0b3JpYWwKClRoaXMgdHV0b3JpYWwgc2ltdWxhdGVzIGEgcG9wdWxhdGlvbiBlZmZlY3Qgc2l6ZSBvZiBDb2hlbidzIGQgPSAwLjUgZm9yIGRpZmZlcmVudCBzYW1wbGUgc2l6ZXMsIGFuZCBleGFtaW5lcyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gQ29oZW4ncyBkLCBpdHMgOTUlIENvbmZpZGVuY2UgSW50ZXJ2YWwsIGFuZCB0aGUgc2lnbmlmaWNhbmNlIG9mIHRoZSB0LXRlc3QncyAqcCotdmFsdWUuIAoKQnkgdGhlIGVuZCBvZiB0aGlzIGxlc3NvbiB5b3Ugc2hvdWxkIHVuZGVyc3RhbmQgdGhhdCAqcCotdmFsdWVzIGFyZSByZS1leHByZXNzaW9ucyBvZiB0aGUgc2FtZSBpbmZvcm1hdGlvbiBjb252ZXllZCBieSBDb25maWRlbmNlIEludGVydmFscywgYW5kIHRoYXQgc3RhdGlzdGljYWwgcG93ZXIgaXMgYSByZS1leHByZXNzaW9uIG9mIHRoZSB3aWR0aCBvZiBDb25maWRlbmNlIEludGVydmFscy4KCiMgQ2l0YXRpb24gJiBMaWNlbnNlCgpDaXRhdGlvbjogCgpJYW4gSHVzc2V5ICgyMDI0KSBJbXByb3ZpbmcgeW91ciBzdGF0aXN0aWNhbCBpbmZlcmVuY2VzIHRocm91Z2ggc2ltdWxhdGlvbiBzdHVkaWVzIGluIFIuIGh0dHBzOi8vZ2l0aHViLmNvbS9pYW5odXNzZXkvc2ltdWxhdGlvbi1jb3Vyc2UKCkxpY2Vuc2U6IAoKW0NDIEJZIDQuMF0oaHR0cHM6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL2xpY2Vuc2VzL2J5LzQuMC9kZWVkLmVuKQoKYGBge3IsIGluY2x1ZGU9RkFMU0V9CgojIHNldCBkZWZhdWx0IGNodW5rIG9wdGlvbnMKa25pdHI6Om9wdHNfY2h1bmskc2V0KG1lc3NhZ2UgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgIHdhcm5pbmcgPSBGQUxTRSkKCiMgZGlzYWJsZSBzY2llbnRpZmljIG5vdGF0aW9uCm9wdGlvbnMoc2NpcGVuID0gOTk5KSAKCmBgYAoKIyBEZXBlbmRlbmNpZXMKCmBgYHtyfQoKbGlicmFyeSh0aWR5cikKbGlicmFyeShkcGx5cikKbGlicmFyeShwdXJycikgCmxpYnJhcnkoc3RyaW5ncikKbGlicmFyeShmb3JjYXRzKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoc2NhbGVzKQpsaWJyYXJ5KHBhdGNod29yaykKbGlicmFyeShrbml0cikKbGlicmFyeShrYWJsZUV4dHJhKQpsaWJyYXJ5KGphbml0b3IpCmxpYnJhcnkoZWZmc2l6ZSkKCmBgYAoKIyBTaW11bGF0aW9uCgpgYGB7cn0KCiMgZnVuY3Rpb25zIGZvciBzaW11bGF0aW9uCmdlbmVyYXRlX2RhdGEgPC0gZnVuY3Rpb24obl9wZXJfY29uZGl0aW9uLAogICAgICAgICAgICAgICAgICAgICAgICAgIG1lYW5fY29udHJvbCwKICAgICAgICAgICAgICAgICAgICAgICAgICBtZWFuX2ludGVydmVudGlvbiwKICAgICAgICAgICAgICAgICAgICAgICAgICBzZCkgewogIAogIGRhdGFfY29udHJvbCA8LSAKICAgIHRpYmJsZShjb25kaXRpb24gPSAiY29udHJvbCIsCiAgICAgICAgICAgc2NvcmUgPSBybm9ybShuID0gbl9wZXJfY29uZGl0aW9uLCBtZWFuID0gbWVhbl9jb250cm9sLCBzZCA9IHNkKSkKICAKICBkYXRhX2ludGVydmVudGlvbiA8LSAKICAgIHRpYmJsZShjb25kaXRpb24gPSAiaW50ZXJ2ZW50aW9uIiwKICAgICAgICAgICBzY29yZSA9IHJub3JtKG4gPSBuX3Blcl9jb25kaXRpb24sIG1lYW4gPSBtZWFuX2ludGVydmVudGlvbiwgc2QgPSBzZCkpCiAgCiAgZGF0YV9jb21iaW5lZCA8LSBiaW5kX3Jvd3MoZGF0YV9jb250cm9sLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGFfaW50ZXJ2ZW50aW9uKSB8PgogICAgbXV0YXRlKGNvbmRpdGlvbiA9IGZjdF9yZWxldmVsKGNvbmRpdGlvbiwgImludGVydmVudGlvbiIsICJjb250cm9sIikpCiAgCiAgcmV0dXJuKGRhdGFfY29tYmluZWQpCn0KCmFuYWx5emUgPC0gZnVuY3Rpb24oZGF0YSkgewoKICByZXNfdF90ZXN0IDwtIHQudGVzdChmb3JtdWxhID0gc2NvcmUgfiBjb25kaXRpb24sIAogICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBkYXRhLAogICAgICAgICAgICAgICAgICAgICAgIHZhci5lcXVhbCA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgYWx0ZXJuYXRpdmUgPSAidHdvLnNpZGVkIikKICAKICByZXNfY29oZW5zX2QgPC0gZWZmc2l6ZTo6Y29oZW4uZChmb3JtdWxhID0gc2NvcmUgfiBjb25kaXRpb24sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IGRhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcG9vbGVkID0gVFJVRSkKICAKICByZXMgPC0gdGliYmxlKHAgPSByZXNfdF90ZXN0JHAudmFsdWUsIAogICAgICAgICAgICAgICAgY29oZW5zX2QgPSByZXNfY29oZW5zX2QkZXN0aW1hdGUsCiAgICAgICAgICAgICAgICBjb2hlbnNfZF9jaV9sb3dlciA9IHJlc19jb2hlbnNfZCRjb25mLmludFsxXSwKICAgICAgICAgICAgICAgIGNvaGVuc19kX2NpX3VwcGVyID0gcmVzX2NvaGVuc19kJGNvbmYuaW50WzJdKQoKICByZXR1cm4ocmVzKQp9CgoKIyBzZXQgc2VlZApzZXQuc2VlZCg0MikKCiMgc2ltdWxhdGlvbiBwYXJhbWV0ZXJzCmV4cGVyaW1lbnRfcGFyYW1ldGVycyA8LSBleHBhbmRfZ3JpZCgKICBuX3Blcl9jb25kaXRpb24gPSBzZXEoZnJvbSA9IDEwLCB0byA9IDkwLCBieSA9IDEwKSwKICBtZWFuX2NvbnRyb2wgPSAwLAogIG1lYW5faW50ZXJ2ZW50aW9uID0gMC41LAogIHNkID0gMSwKICBpdGVyYXRpb24gPSAxOjEwMDAKKSAKCiMgcnVuIHNpbXVsYXRpb24Kc2ltdWxhdGlvbiA8LSBleHBlcmltZW50X3BhcmFtZXRlcnMgfD4KICBtdXRhdGUoZ2VuZXJhdGVkX2RhdGEgPSBwbWFwKGxpc3Qobl9wZXJfY29uZGl0aW9uLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWVhbl9jb250cm9sLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZWFuX2ludGVydmVudGlvbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2QpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2VuZXJhdGVfZGF0YSkpIHw+CiAgbXV0YXRlKHJlc3VsdHMgPSBwbWFwKGxpc3QoZ2VuZXJhdGVkX2RhdGEpLAogICAgICAgICAgICAgICAgICAgICAgICBhbmFseXplKSkKCmBgYAoKIyBDb2hlbidzIGQgYnkgc2FtcGxlIHNpemUKCmBgYHtyIGZpZy5oZWlnaHQ9NSwgZmlnLndpZHRoPTZ9CgojIHdyYW5nbGUKc2ltdWxhdGlvbiB8PgogIHVubmVzdChyZXN1bHRzKSB8PgogICMgcGxvdAogIGdncGxvdChhZXMobl9wZXJfY29uZGl0aW9uKjIsIGNvaGVuc19kKSkgKwogIGdlb21faml0dGVyKGFscGhhID0gMC4yNSkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGxpbmV0eXBlID0gImRvdHRlZCIpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gYnJlYWtzX3ByZXR0eShuID0gMTApLAogICAgICAgICAgICAgICAgICAgICBuYW1lID0gIlRvdGFsIE4iKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IGJyZWFrc19wcmV0dHkobiA9IDEwKSwKICAgICAgICAgICAgICAgICAgICAgI2xpbWl0cyA9IGMoMCwxKSwKICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJDb2hlbidzIGQiKSArCiAgdGhlbWVfbGluZWRyYXcoKSArCiAgc2NhbGVfY29sb3JfdmlyaWRpc19kKGJlZ2luID0gMC4zLCBlbmQgPSAwLjcpICsKICBnZ3RpdGxlKCJQb3B1bGF0aW9uIENvaGVuJ3MgZCA9IDAuNSIpCgpgYGAKCi0gRWFjaCBwb2ludCBpcyBhIENvaGVuJ3MgZCBmcm9tIG9uZSBkYXRhc2V0IChpLmUuLCBvbmUgaXRlcmF0aW9uKS4KLSBUaGUgcG9wdWxhdGlvbiBDb2hlbidzIGQgaXMgMC41LiBOb3RpY2UgaG93IHRoZSBkaXN0cmlidXRpb24gb2Ygc2FtcGxlIENvaGVuJ3MgZCB2YWx1ZXMgdmFyeSBieSBzYW1wbGUgc2l6ZTogQ29oZW4ncyBkIHZhbHVlcyBhYm92ZSAyLjAgYW5kIGJlbG93IC0wLjUgYXJlIG9ic2VydmVkIHdoZW4gc2FtcGxlIHNpemUgaXMgdmVyeSBzbWFsbC4gQXMgc2FtcGxlIHNpemUgZ2V0cyBsYXJnZXIsIHRoZXkgc3RhYmlsaXplLiBIb3dldmVyLCBpbmRpdmlkdWFsIGRhdGFzZXRzIHN0aWxsIGdlbmVyYXRlIENvaGVuJ3MgZCB2YWx1ZXMgdGhhdCBkaXZlcmdlIHN1YnN0YW50aWFsbHkgZnJvbSB0aGUgdHJ1ZSB2YWx1ZSBvZiAwLjUuCgojIyBPcmRlcmVkIGJ5IHN0YXRpc3RpY2FsIHNpZ25pZmljYW5jZQoKTGV0J3MgY29sb3IgdGhlIENvaGVuJ3MgZHMgYnkgdGhlaXIgc3RhdGlzdGljYWwgc2lnbmlmaWNhbmNlLiAKCmBgYHtyIGZpZy5oZWlnaHQ9NSwgZmlnLndpZHRoPTZ9CgojIHdyYW5nbGUKc2ltdWxhdGlvbiB8PgogIHVubmVzdChyZXN1bHRzKSB8PgogIG11dGF0ZShzaWduaWZpY2FudCA9IHAgPCAuMDUpIHw+CiAgIyBwbG90CiAgZ2dwbG90KGFlcyhuX3Blcl9jb25kaXRpb24qMiwgY29oZW5zX2QsIGNvbG9yID0gc2lnbmlmaWNhbnQpKSArCiAgZ2VvbV9qaXR0ZXIoYWxwaGEgPSAwLjI1KSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwgbGluZXR5cGUgPSAiZG90dGVkIikgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBicmVha3NfcHJldHR5KG4gPSAxMCksCiAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiVG90YWwgTiIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gYnJlYWtzX3ByZXR0eShuID0gMTApLAogICAgICAgICAgICAgICAgICAgICAjbGltaXRzID0gYygwLDEpLAogICAgICAgICAgICAgICAgICAgICBuYW1lID0gIkNvaGVuJ3MgZCIpICsKICB0aGVtZV9saW5lZHJhdygpICsKICBzY2FsZV9jb2xvcl92aXJpZGlzX2QoYmVnaW4gPSAwLjMsIGVuZCA9IDAuNywgZGlyZWN0aW9uID0gLTEpICsKICBnZ3RpdGxlKCJQb3B1bGF0aW9uIENvaGVuJ3MgZCA9IDAuNSIpCgpgYGAKCi0gVGhlcmUgaXMgY2xlYXJseSBhIGN1dC1vZmYgdmFsdWUgZm9yIGVhY2ggc2FtcGxlIHNpemUgdGhhdCBkZXRlcm1pbmVzIHdoZW4gYSBDb2hlbidzIGQgdmFsdWUgaXMgYXNzb2NpYXRlZCB3aXRoIGEgc2lnbmlmaWNhbnQgdnMuIG5vbi1zaWduaWZpY2FudCB0LXRlc3QgcC12YWx1ZS4gV2hhdCBkZXRlcm1pbmVzIHRoaXMgY3V0LW9mZj8gRG8geW91IGhhdmUgYW4gaW50dWl0aW9uIGZvciBpdD8KCiMjIEF2ZXJhZ2UgZWZmZWN0IHNpemUgZm9yIHNpZ25pZmljYW50IGVmZmVjdHMKClF1aWNrIGFzaWRlOiBTaWduaWZpY2FudCByZXN1bHRzIGFyZSBtb3JlIGxpa2VseSB0byBiZSBwdWJsaXNoZWQgdGhhbiBub24gc2lnbmlmaWNhbnQgb25lcy4gSWYgd2Ugb25seSBsb29rIGF0IHRoZSBzaWduaWZpY2FudCByZXN1bHRzLCB3aGF0IGlzIHRoZSBhdmVyYWdlIGVmZmVjdCBzaXplIGJ5IE4/CgpQbG90IG9ubHkgdGhlIHNpZ25pZmljYW50IGVmZmVjdCBzaXplcywgYW5kIGFkZCB0aGVpciBtZWFucy4KCmBgYHtyIGZpZy5oZWlnaHQ9NSwgZmlnLndpZHRoPTZ9CgojIHdyYW5nbGUKc2ltdWxhdGlvbiB8PgogIHVubmVzdChyZXN1bHRzKSB8PgogIG11dGF0ZShzaWduaWZpY2FudCA9IHAgPCAuMDUpIHw+CiAgZmlsdGVyKHNpZ25pZmljYW50KSB8PgogICMgcGxvdAogIGdncGxvdChhZXMobl9wZXJfY29uZGl0aW9uKjIsIGNvaGVuc19kLCBjb2xvciA9IHNpZ25pZmljYW50KSkgKwogIGdlb21faml0dGVyKGFscGhhID0gMC4yNSkgKwogICMgYWRkIG1lYW4gcG9pbnRzIAogIHN0YXRfc3VtbWFyeShmdW4gPSAibWVhbiIsCiAgICAgICAgICAgICAgIGdlb20gPSAicG9pbnQiLAogICAgICAgICAgICAgICBzaXplID0gMywKICAgICAgICAgICAgICAgY29sb3IgPSAiIzM1NjA4REZGIikgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGxpbmV0eXBlID0gImRvdHRlZCIpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gYnJlYWtzX3ByZXR0eShuID0gMTApLAogICAgICAgICAgICAgICAgICAgICBuYW1lID0gIlRvdGFsIE4iKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IGJyZWFrc19wcmV0dHkobiA9IDEwKSwKICAgICAgICAgICAgICAgICAgICAgI2xpbWl0cyA9IGMoMCwxKSwKICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJDb2hlbidzIGQiKSArCiAgdGhlbWVfbGluZWRyYXcoKSArCiAgc2NhbGVfY29sb3JfdmlyaWRpc19kKGJlZ2luID0gMC4zLCBlbmQgPSAwLjcsIGRpcmVjdGlvbiA9IC0xKSArCiAgZ2d0aXRsZSgiUG9wdWxhdGlvbiBDb2hlbidzIGQgPSAwLjUiKQoKYGBgCgotIEJhY2sgdG8gdGhlIHE6IFRoZXJlIGlzIGNsZWFybHkgYSBjdXQtb2ZmIHZhbHVlIGZvciBlYWNoIHNhbXBsZSBzaXplIHRoYXQgZGV0ZXJtaW5lcyB3aGVuIGEgQ29oZW4ncyBkIHZhbHVlIGlzIGFzc29jaWF0ZWQgd2l0aCBhIHNpZ25pZmljYW50IHZzLiBub24tc2lnbmlmaWNhbnQgdC10ZXN0IHAtdmFsdWUuIFdoYXQgZGV0ZXJtaW5lcyB0aGlzIGN1dC1vZmY/IERvIHlvdSBoYXZlIGFuIGludHVpdGlvbiBmb3IgaXQ/CgojIENvaGVuJ3MgZCBhbmQgaXRzIDk1JSBDSXMgCgpUbyB1bmRlcnN0YW5kIGl0IGZ1cnRoZXIsIGxldCdzIGFkZCB0aGUgOTUlIENvbmZpZGVuY2UgSW50ZXJ2YWxzLgoKIyMgUmFuZG9tbHkgc2VsZWN0IG9uZSBkYXRhc2V0IHBlciBzYW1wbGUgc2l6ZQoKVG8gdW5kZXJzdGFuZCB0aGUgcGxvdCwgd2UnbGwgcGxvdCBqdXN0IG9uZSBkYXRhIHNldCAoaXRlcmF0aW9uKSBmb3IgZWFjaCBzYW1wbGUgc2l6ZS4KClJlLXJ1biB0aGUgY2h1bmsgdG8gc2VsZWN0IG5ldyByYW5kb20gdmFsdWVzLiBOb3RpY2UgaG93IHRoZSBtYWduaXR1ZGUgb2YgdGhlIENvaGVuJ3MgZCB2YWx1ZXMgJ2RhbmNlJyBhcm91bmQgYmV0d2VlbiBkYXRhIHNldHMgZHVlIHRvIHJhbmRvbSBzYW1wbGluZyBlcnJvciwgYnV0IHRoZSB3aWR0aCBvZiB0aGUgQ29uZmlkZW5jZSBJbnRlcnZhbHMgZG8gbm90LiBDb25maWRlbmNlIHdpZHRoIGlzIHN0ZWFkeSBhcyBpdCBpcyBhIGZ1bmN0aW9uIG9mIHNhbXBsZSBzaXplLgoKYGBge3J9CgojIHdyYW5nbGUKc2ltdWxhdGlvbiB8PgogIHVubmVzdChyZXN1bHRzKSB8PgogIG11dGF0ZShzaWduaWZpY2FudCA9IHAgPCAuMDUpIHw+CiAgIyByYW5kb21seSBzYW1wbGUgMSBlZmZlY3RzIHNpemUgcGVyIHNhbXBsZSBzaXplCiAgZ3JvdXBfYnkobl9wZXJfY29uZGl0aW9uKSB8PgogIHNsaWNlX3NhbXBsZShuID0gMSkgfD4KICB1bmdyb3VwKCkgfD4KICAjIHBsb3QKICBnZ3Bsb3QoYWVzKG5fcGVyX2NvbmRpdGlvbioyLCBjb2hlbnNfZCwgY29sb3IgPSBzaWduaWZpY2FudCkpICsKICBnZW9tX3BvaW50KCkgKwogICMgOTUlIENJcwogIGdlb21fbGluZXJhbmdlKGFlcyh5bWluID0gY29oZW5zX2RfY2lfbG93ZXIsIHltYXggPSBjb2hlbnNfZF9jaV91cHBlcikpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLCBsaW5ldHlwZSA9ICJkb3R0ZWQiKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IGJyZWFrc19wcmV0dHkobiA9IDEwKSwKICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJUb3RhbCBOIikgKwogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBicmVha3NfcHJldHR5KG4gPSAxMCksCiAgICAgICAgICAgICAgICAgICAgICNsaW1pdHMgPSBjKDAsMSksCiAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiQ29oZW4ncyBkIikgKwogIHRoZW1lX2xpbmVkcmF3KCkgKwogIHNjYWxlX2NvbG9yX3ZpcmlkaXNfZChiZWdpbiA9IDAuMywgZW5kID0gMC43KSArCiAgZ2d0aXRsZSgiUG9wdWxhdGlvbiBDb2hlbidzIGQgPSAwLjVcbk9uZSByYW5kb21seSBzZWxlY3RlZCBkYXRhc2V0IHBlciBzYW1wbGUgc2l6ZSIpCgpgYGAKCi0gU2lnbmlmaWNhbnQgd2hlbiA5NSUgQ0kgZXhjbHVkZXMgemVybyBlZmZlY3Qgc2l6ZQoKIyMgQWxsIGRhdGFzZXRzCgpOb3cgbGV0J3MgcGxvdCBhbGwgdGhlIGRhdGEgc2V0cyAoaXRlcmF0aW9ucykuCgpgYGB7ciBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9MTJ9CgojIHdyYW5nbGUKc2ltdWxhdGlvbiB8PgogIHVubmVzdChyZXN1bHRzKSB8PgogIG11dGF0ZShzaWduaWZpY2FudCA9IHAgPCAuMDUsCiAgICAgICAgIHRvdGFsX24gPSBwYXN0ZSgiTiA9Iiwgbl9wZXJfY29uZGl0aW9uKjIpLAogICAgICAgICB0b3RhbF9uID0gZmN0X3Jlb3JkZXIodG90YWxfbiwgYXMubnVtZXJpYyhzdHJfZXh0cmFjdCh0b3RhbF9uLCAiXFxkKyIpKSkpIHw+CiAgIyBwbG90CiAgZ2dwbG90KGFlcyhpdGVyYXRpb24sIGNvaGVuc19kLCBjb2xvciA9IHNpZ25pZmljYW50KSkgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjUpICsKICBnZW9tX2xpbmVyYW5nZShhZXMoeW1pbiA9IGNvaGVuc19kX2NpX2xvd2VyLCB5bWF4ID0gY29oZW5zX2RfY2lfdXBwZXIpLCBhbHBoYSA9IDAuMikgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGxpbmV0eXBlID0gImRvdHRlZCIpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gYnJlYWtzX3ByZXR0eShuID0gMTApLAogICAgICAgICAgICAgICAgICAgICBuYW1lID0gIkl0ZXJhdGlvbiIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gYnJlYWtzX3ByZXR0eShuID0gMTApLAogICAgICAgICAgICAgICAgICAgICAjbGltaXRzID0gYygwLDEpLAogICAgICAgICAgICAgICAgICAgICBuYW1lID0gIkNvaGVuJ3MgZCIpICsKICB0aGVtZV9saW5lZHJhdygpICsKICBzY2FsZV9jb2xvcl92aXJpZGlzX2QoYmVnaW4gPSAwLjMsIGVuZCA9IDAuNykgKwogIGZhY2V0X3dyYXAofiB0b3RhbF9uLCBuY29sID0gMywgc2NhbGVzID0gImZyZWVfeSIpCgpgYGAKCiMjIE9yZGVyZWQgYnkgQ29oZW4ncyBkCgpUaGUgYWJvdmUgcGxvdCBpcyBoYXJkIHRvIHVuZGVyc3RhbmQuIExldCdzIG9yZGVyIHRoZSBDb2hlbidzIGRzIGZyb20gc21hbGxlc3QgdG8gbGFyZ2VzdCwgc28gdGhlIHNtYWxsIG9uZXMgYXJlIG9uIHRoZSBsZWZ0IGFuZCB0aGUgbGFyZ2Ugb25lcyBhcmUgb24gdGhlIHJpZ2h0LgoKYGBge3IgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTEyfQoKIyB3cmFuZ2xlCnNpbXVsYXRpb24gfD4KICB1bm5lc3QocmVzdWx0cykgfD4KICBtdXRhdGUoc2lnbmlmaWNhbnQgPSBwIDwgLjA1LAogICAgICAgICB0b3RhbF9uID0gcGFzdGUoIk4gPSIsIG5fcGVyX2NvbmRpdGlvbioyKSwKICAgICAgICAgdG90YWxfbiA9IGZjdF9yZW9yZGVyKHRvdGFsX24sIGFzLm51bWVyaWMoc3RyX2V4dHJhY3QodG90YWxfbiwgIlxcZCsiKSkpKSB8PgogIGFycmFuZ2Uobl9wZXJfY29uZGl0aW9uLCBjb2hlbnNfZCkgfD4KICBncm91cF9ieShuX3Blcl9jb25kaXRpb24pIHw+CiAgbXV0YXRlKHJhbmsgPSByb3dfbnVtYmVyKCkpIHw+CiAgdW5ncm91cCgpIHw+CiAgIyBwbG90CiAgZ2dwbG90KGFlcyhyYW5rLCBjb2hlbnNfZCwgY29sb3IgPSBzaWduaWZpY2FudCkpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fbGluZXJhbmdlKGFlcyh5bWluID0gY29oZW5zX2RfY2lfbG93ZXIsIHltYXggPSBjb2hlbnNfZF9jaV91cHBlciksIGFscGhhID0gMC4yKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwgbGluZXR5cGUgPSAiZG90dGVkIikgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBicmVha3NfcHJldHR5KG4gPSAxMCksCiAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiUmFua2VkIGl0ZXJhdGlvbiIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gYnJlYWtzX3ByZXR0eShuID0gMTApLAogICAgICAgICAgICAgICAgICAgICAjbGltaXRzID0gYygwLDEpLAogICAgICAgICAgICAgICAgICAgICBuYW1lID0gIkNvaGVuJ3MgZCIpICsKICB0aGVtZV9saW5lZHJhdygpICsKICBzY2FsZV9jb2xvcl92aXJpZGlzX2QoYmVnaW4gPSAwLjMsIGVuZCA9IDAuNykgKwogIGZhY2V0X3dyYXAofiB0b3RhbF9uLCBuY29sID0gMykKCmBgYAoKLSBOb3RpY2UgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIDk1JSBDSSBhbmQgcCB2YWx1ZSBzaWduaWZpY2FuY2UuCi0gQSBjZXJ0YWluIHBlcmNlbnRhZ2Ugb2YgQ29oZW4ncyBkcyBhcmUgZ3JlZW4gdnMuIGJsdWUuIFdoYXQgaXMgdGhpcyBwZXJjZW50YWdlIGFsc28gY2FsbGVkPyBXaGF0IHN0YXRpc3RpY2FsIHByb3BlcnR5PwoKIyBNZWFuIENvaGVuJ3MgZCBhbmQgbWVhbiBpbnRlcnZhbCB3aWR0aCBieSBzYW1wbGUgc2l6ZQoKTm93IGxldCdzIGF2ZXJhZ2Ugb3ZlciB0aGUgQ29oZW4ncyBkcyBpbiBlYWNoIGNvbmRpdGlvbiB0byBmaW5kIHRoZSBtZWFuIENvaGVuJ3MgZCBhbmQgaXRzIG1lYW4gOTUlIENJcy4KCmBgYHtyIGZpZy5oZWlnaHQ9Mi41LCBmaWcud2lkdGg9Nn0KCiMgd3JhbmdsZQpzaW11bGF0aW9uX3N1bW1hcnkgPC0gc2ltdWxhdGlvbiB8PgogICMgdW5uZXN0IHJlc3VsdHMKICB1bm5lc3QocmVzdWx0cykgfD4KICBncm91cF9ieShuX3Blcl9jb25kaXRpb24pIHw+CiAgc3VtbWFyaXplKHByb3BvcnRpb25fc2lnbmlmaWNhbnQgPSBtZWFuKHAgPCAuMDUpLCAKICAgICAgICAgICAgbWVhbl9jb2hlbnNfZCA9IG1lYW4oY29oZW5zX2QpLAogICAgICAgICAgICBtZWFuX2NvaGVuc19kX2NpX2xvd2VyID0gbWVhbihjb2hlbnNfZF9jaV9sb3dlciksCiAgICAgICAgICAgIG1lYW5fY29oZW5zX2RfY2lfdXBwZXIgPSBtZWFuKGNvaGVuc19kX2NpX3VwcGVyKSkgfD4KICBtdXRhdGUoY2VudGVyZWRfbWVhbl9jb2hlbnNfZF9jaV9sb3dlciA9IG1lYW5fY29oZW5zX2RfY2lfbG93ZXIgLSBtZWFuX2NvaGVuc19kLAogICAgICAgICBjZW50ZXJlZF9tZWFuX2NvaGVuc19kX2NpX3VwcGVyID0gbWVhbl9jb2hlbnNfZF9jaV91cHBlciAtIG1lYW5fY29oZW5zX2QpCgojIHBsb3QgcmVzdWx0cwpwMSA8LSBnZ3Bsb3Qoc2ltdWxhdGlvbl9zdW1tYXJ5LCBhZXMobl9wZXJfY29uZGl0aW9uKjIsIG1lYW5fY29oZW5zX2QpKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX2xpbmVyYW5nZShhZXMoeW1pbiA9IG1lYW5fY29oZW5zX2RfY2lfbG93ZXIsIHltYXggPSBtZWFuX2NvaGVuc19kX2NpX3VwcGVyKSkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGxpbmV0eXBlID0gImRvdHRlZCIpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gYnJlYWtzX3ByZXR0eShuID0gMTApLAogICAgICAgICAgICAgICAgICAgICBuYW1lID0gIlRvdGFsIHNhbXBsZSBzaXplIikgKwogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBicmVha3NfcHJldHR5KG4gPSA1KSwKICAgICAgICAgICAgICAgICAgICAgI2xpbWl0cyA9IGMoMCwxKSwKICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJNZWFuIENvaGVuJ3MgZFxuKGFuZCBtZWFuIDk1JSBDSXMpIikgKwogIHRoZW1lX2xpbmVkcmF3KCkgKwogIGdndGl0bGUoIlBvcHVsYXRpb24gQ29oZW4ncyBkID0gMC41IikKCnAxCgpgYGAKCiMgTWVhbiBDb2hlbidzIGQgYW5kIHBvd2VyIGJ5IHNhbXBsZSBzaXplCgpgYGB7ciBmaWcuaGVpZ2h0PTUsIGZpZy53aWR0aD02fQoKcDIgPC0gZ2dwbG90KHNpbXVsYXRpb25fc3VtbWFyeSwgYWVzKG5fcGVyX2NvbmRpdGlvbioyLCBwcm9wb3J0aW9uX3NpZ25pZmljYW50KSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMC4wNSwgbGluZXR5cGUgPSAiZG90dGVkIikgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAuODAsIGxpbmV0eXBlID0gImRvdHRlZCIpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gYnJlYWtzX3ByZXR0eShuID0gMTApLAogICAgICAgICAgICAgICAgICAgICBuYW1lID0gIlRvdGFsIHNhbXBsZSBzaXplIikgKwogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBicmVha3NfcHJldHR5KG4gPSA1KSwKICAgICAgICAgICAgICAgICAgICAgbGltaXRzID0gYygwLDEpLAogICAgICAgICAgICAgICAgICAgICBuYW1lID0gIlByb3BvcnRpb24gb2Ygc2lnbmlmaWNhbnRcbnAtdmFsdWVzIikgKwogIHRoZW1lX2xpbmVkcmF3KCkgCgpwMSArIHAyICsgcGxvdF9sYXlvdXQobmNvbCA9IDEpCgpgYGAKCiMgRXhlcmNpc2U6IFBvd2VyIGZvciBlcXVpdmFsZW5jZSB0ZXN0CgpTZWUgTGFrZW5zIGV0IGFsLiwgMjAxOCBmb3IgYSB0dXRvcmlhbCBvbiB0aGUgY29uY2VwdHMgYmVsb3cuCgpVc2luZyBhIFNtYWxsZXN0IEVmZmVjdCBTaXplIG9mIEludGVyZXN0IChTRVNPSSkgb2YgQ29oZW4ncyBkID0gMC4yLCB3aGF0IGlzIHRoZSBwb3dlciBvZiBhIFR3byBPbmUtU2lkZWQgRXF1aXZhbGVuY2UgVGVzdCAoVE9TVCkgZm9yIGRpZmZlcmVudCBzYW1wbGUgc2l6ZXM/IFdoYXQgTiBpcyBuZWVkZWQgZm9yIDgwJSBwb3dlciB0byBkZXRlY3QgYSB0cnVlIG51bGwgZWZmZWN0IHNpemUgYXMgZXF1aXZhbGVudCB0byB6ZXJvPwoKTm90ZTogYmVjYXVzZSByZWFzb25zLCB1c2UgdGhlIDkwJSBDb25maWRlbmNlIEludGVydmFsIGluc3RlYWQgb2YgOTUgKHNlZSBMYWtlbnMgZXQgYWwuLCAyMDE4KS4KCmBgYHtyfQoKaWYoZmlsZS5leGlzdHMoIm1hdGVyaWFscy9sYWtlbnMgZXQgYWwgMjAxOCBmaWd1cmUgMS5wbmciKSl7CiAga25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoIm1hdGVyaWFscy9sYWtlbnMgZXQgYWwgMjAxOCBmaWd1cmUgMS5wbmciKQp9CgpgYGAKCiMgU2Vzc2lvbiBpbmZvCgpgYGB7cn0KCnNlc3Npb25JbmZvKCkKCmBgYAoKCg==